﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using si = Golem.Indexer.Search;
using Golem.Indexer.Indexers;
using System.Threading;
using System.Threading.Tasks;
using Golem.Indexer.Data;
using Golem.Indexer.Search;

namespace Golem.Indexer
{
    public class IndexingService
    {
        public delegate void IndexingServiveHandler();
        public event IndexingServiveHandler IndexingStarted;
        public event IndexingServiveHandler IndexingEnded;
        public si.ISearch Search { get; private set; }
        public Dictionary<Protocol, IIndexer> Indexers { get; private set; }
        public IDatabase Database { get; set; }
        public bool Running { get { return running; } }
        public bool AutoFillDatabase
        {
            get { return this.autofilldatabase; }
            set 
            { 
                this.autofilldatabase = value;

                if (value)
                {
                    this.Search.MachineAlive -= new MachineHandler(Search_MachineAlive);
                    this.Search.MachineAlive += new MachineHandler(Search_MachineAlive);
                    this.Search.MachinePingCompleted -= new MachineHandler(Search_MachinePingCompleted);
                    this.Search.MachinePingCompleted += new MachineHandler(Search_MachinePingCompleted);
                }
                else
                {
                    this.Search.MachineAlive -= new MachineHandler(Search_MachineAlive);
                    this.Search.MachinePingCompleted -= new MachineHandler(Search_MachinePingCompleted);
                }

                foreach (var indexer in this.Indexers)
                {
                    if (value)
                    {
                        indexer.Value.FilesIndexed -= new IndexerFileHandler(Value_FilesIndexed);
                        indexer.Value.FilesIndexed += new IndexerFileHandler(Value_FilesIndexed);
                        indexer.Value.ProtocolIndexingStarted -= new IndexerProtocolHandler(Value_ProtocolIndexingStarted);
                        indexer.Value.ProtocolIndexingStarted += new IndexerProtocolHandler(Value_ProtocolIndexingStarted);
                    }
                    else
                    {
                        indexer.Value.FilesIndexed -= new IndexerFileHandler(Value_FilesIndexed);
                        indexer.Value.ProtocolIndexingStarted -= new IndexerProtocolHandler(Value_ProtocolIndexingStarted);
                    }

                    indexer.Value.ProtocolIndexingEnded -= new IndexerProtocolHandler(Value_ProtocolIndexingEnded);
                    indexer.Value.ProtocolIndexingEnded += new IndexerProtocolHandler(Value_ProtocolIndexingEnded);
                }
            }
        }

        private bool quit = false;
        private bool running = false;
        private int delay = 1000 * 60 * 1;
        private bool autofilldatabase = true;
        private Dictionary<MachineDescriptor, int> indexedProtocolsCount = new Dictionary<MachineDescriptor,int>();
        private int indexedMachinesCount = 0;
        private ManualResetEvent indexSync = new ManualResetEvent(false);
        private bool scanCompleted = false;

        public IndexingService(int delay = 1000 * 60 * 10, bool autoFillDatabase = true)
        {
            this.Search = new si.Search();

            this.Search.SearchScanCompleted += new SearchHandler(Search_SearchScanCompleted);

            this.Indexers = new Dictionary<Protocol, IIndexer>();
            this.Indexers.Add(Protocol.Ftp, new FtpIndexer());
            this.Indexers.Add(Protocol.Samba, new SambaIndexer());

            this.Indexers[Protocol.Ftp].ProtocolIndexingErrorThrowed += new IndexerErrorHandler(IndexingService_ProtocolIndexingErrorThrowed);
            this.Indexers[Protocol.Samba].ProtocolIndexingErrorThrowed += new IndexerErrorHandler(IndexingService_ProtocolIndexingErrorThrowed);

            this.Indexers[Protocol.Ftp].RunIndexing();
            this.Indexers[Protocol.Samba].RunIndexing();

            this.delay = delay;
            this.AutoFillDatabase = autoFillDatabase;
        }

        void IndexingService_ProtocolIndexingErrorThrowed(MachineDescriptor machine, Protocol protocol, Exception ex)
        {
            string log = String.Format("{0} - {1} - {2} - {3}", machine.IP, machine.Hostname, Enum.GetName(typeof(Protocol), protocol), ex.Message);
            this.WriteToLog(log);
        }

        private void WriteToLog(String text)
        {
            System.IO.File.AppendAllText("log.txt", text);
        }

        public void Start()
        {
            if (this.running)
                throw new Exception("You must stop indexing before you proceed new start");

            quit = false;

            Thread thread = new Thread(new ThreadStart(ReIndexing));
            thread.Start();
        }

        public void WaitUntilExit()
        {
            System.Threading.Thread.CurrentThread.Join();
        }

        private void ReIndexing()
        {
            while (!quit)
            {
                running = true;

                if (IndexingStarted != null)
                    IndexingStarted();

                this.indexSync.Reset();

                this.Search.ReloadScanNetworks();
                this.Search.ScanNetworks();

                this.indexSync.WaitOne();
                scanCompleted = false;

                if (IndexingEnded != null)
                    IndexingEnded();

                if (this.delay == 0)
                    quit = true;
                else
                    Thread.Sleep(this.delay);
            }

            this.running = false;
        }

        public void Stop()
        {
            quit = true;

            if (this.Database != null)
            {
                this.Database.Close();
                this.Database = null;
            }
            
            Parallel.ForEach(this.Indexers, indexer =>
            {
                indexer.Value.StopIndexing();
            });
        }

        private void Search_MachinePingCompleted(MachineDescriptor machine)
        {
            if (this.Database != null)
                this.Database.UpdateMachineState(machine);
        }

        private void Search_MachineAlive(MachineDescriptor machine)
        {
            Console.WriteLine(String.Format("Jdu indexovat stroj: {0} - {1}", machine.IP, machine.Hostname));
            foreach (var indexer in this.Indexers)
            {
                indexer.Value.Add(machine);
            }
        }

        private void Value_ProtocolIndexingStarted(MachineDescriptor machine, Protocol protocol)
        {
            if (this.Database == null)
                return;

            if (!this.Database.IsDatabaseReady())
                return;

            this.Database.CreateProtocol(machine, protocol);
        }

        private void Value_FilesIndexed(MachineDescriptor machine, Protocol protocol, List<FileDescriptor> files)
        {
            if (this.Database == null)
                return;

            if (!this.Database.IsDatabaseReady())
                return;

            this.Database.WriteFiles(machine, protocol, files);
        }

        void Search_SearchScanCompleted()
        {
            scanCompleted = true;
        }

        void Value_ProtocolIndexingEnded(MachineDescriptor machine, Protocol protocol)
        {
            this.Database.DoPermanentProtocolData(machine, protocol);

            lock (this.indexedProtocolsCount)
            {
                if (!this.indexedProtocolsCount.ContainsKey(machine))
                    this.indexedProtocolsCount.Add(machine, 0);

                this.indexedProtocolsCount[machine]++;

                if (this.indexedProtocolsCount[machine] == this.Indexers.Count)
                {
                    this.indexedMachinesCount++;
                    this.indexedProtocolsCount.Remove(machine);

                    Console.WriteLine(String.Format("Indexovano {0}/{1}/{2} stroju.", indexedMachinesCount, this.Search.OnlineComputerCount, this.Search.ComputerCounts));
                }
            }

            var protocolName = Enum.GetName(typeof(Golem.Indexer.Data.Protocol), protocol);
            Console.WriteLine(String.Format("Doindexoval jsem protokol {0} na stroji: {1} - {2}", protocolName, machine.IP, machine.Hostname));

            if (scanCompleted)
            {
                if (this.Indexers[Protocol.Ftp].RemainingMachinesCount > 0 || this.Indexers[Protocol.Samba].RemainingMachinesCount > 0)
                    return;

                WriteToLog("==== Dokonceno indexovani ====");

                this.indexSync.Set();
            }
        }
    }
}
